#include "engine.hpp"

#include <algorithm>

#include "errors.hpp"
#include "DebugManager.hpp"

namespace wolf {
using namespace std;
//////////////////////////////////
//Constructors and destuctrors  //
//////////////////////////////////
Engine::Engine(RunMode mode) {
	runMode = mode;
	screen = NULL;
	frameRate = 0;
	oldTime = 0;
	frame = 0;
	if (instance)
		throw SingletonError(
				"There is another Engine instance already running");
	instance = this;
}

Engine::~Engine(void) {
	end();
}

////////////////////
// Static Members //
////////////////////
Engine *Engine::instance = NULL;

//////////////////////////////////
// Engine Configuration         //
//////////////////////////////////
void Engine::setVideoMode(unsigned width, unsigned height, unsigned bpp) {
	screen = SDL_SetVideoMode(width, height, bpp,
			SDL_HWSURFACE | SDL_ANYFORMAT);
	if (screen == NULL)
		throw SdlError(SDL_GetError());
}

void Engine::prepare(void) {
	// initialize SDL video
	if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
		throw SdlError();
	}
	// make sure SDL cleans up before exit
	atexit(SDL_Quit);

	if (TTF_Init() < 0) {
		throw SdlError();
	}
	// make sure SDL cleans up before exit
	atexit(TTF_Quit);
}

void Engine::start(void) {
	if (frameRate && screen) {
		running = true;
//            addDrawHandler(defaultDrawHandler);
		if (runMode == DEBUG)
			addManager(*new DebugManager, INSERT_AT_BEGIN);
		WOLF_FOR( list<Manager*>, handlers, it ) {
			(*it)->onSetup(*this);
		}
	} else
		throw EngineError(frameRate, screen);
}

void Engine::end(void) {
	WOLF_FOR( list<Manager*>, handlers, it ) {
		(*it)->onCleanup(*this);
	}
	running = false;
}

bool Engine::isRunning(void) {
	return running;
}

void Engine::halt(void) {
	SDL_Event quitEvent;
	quitEvent.type = SDL_QUIT;
	SDL_PushEvent(&quitEvent);
}

void Engine::doFrame(void) {
	// Actualizing the frame
	WOLF_FOR( list<Manager*>, handlers, it ) {
		(*it)->onStartFrame(*this);
	}

	SDL_Event event;
	while (SDL_PollEvent(&event)) {
		switch (event.type) {
		case SDL_QUIT:
			WOLF_FOR( list<Manager*>, handlers, it ) {
				if (!(*it)->onQuit(*this))
					goto DONT_QUIT;
			}
			end();
			return;
			break;
		default:
			WOLF_FOR( list<Manager*>, handlers, it ) {
				if (!(*it)->onEvent(*this, event))
					break;
			}
			break;
		}
		//Yes, it is a label. I know I should not use that but it just works for now.
		DONT_QUIT: continue;
	}
	// Actualizing the frame
	WOLF_FOR( list<Manager*>, handlers, it ) {
		(*it)->onFrame(*this);
	}

	// Repainting the Screen
	SDL_FillRect(screen, NULL, 0);
//	WOLF_FOR( set<Layer*>, layers , it ) {
//		(*it)->draw(screen);
//	}
	WOLF_RFOR( list<Manager*>, handlers, it ) {
		(*it)->onDraw(*this, screen);
	}
	SDL_Flip(screen);

	// Frame delay time evaluation
	int delayTime = 1000 / frameRate;
	int time = SDL_GetTicks();
	int time2wait = delayTime - (time - oldTime);
	SDL_Delay(time2wait > 0 ? time2wait : 0);
	oldTime = SDL_GetTicks();

	//Incrementing the frame count
	++frame;
}

/////////////////////////
// Getters and Setters //
/////////////////////////

void Engine::setFrameRate(unsigned fps) {
	frameRate = fps;
}

unsigned Engine::getFrameRate(void) {
	return frameRate;
}

Engine::RunMode Engine::getRunMode(void) {
	return runMode;
}

void Engine::setRunMode(RunMode mode) {
	runMode = mode;
}

unsigned Engine::getCurrentFrame() {
	return frame;
}

unsigned Engine::getCurrentFrameTicks() const {
	return oldTime;
}

void Engine::setScreen(SDL_Surface *screen_) {
	screen = screen_;
}

SDL_Surface *Engine::getScreen() {
	return screen;
}

Uint8 *Engine::getKeyState(int *numkeys) {
	return SDL_GetKeyState(numkeys);
}

///////////////////////////
// Event Handling System //
///////////////////////////
void Engine::addManager(Manager &handler, int order) {
	if (order == INSERT_AT_END)
		handlers.push_back(&handler);
	else
		handlers.push_front(&handler);
	if(running)
		handler.onSetup(*this);
}

void Engine::removeManager(Manager &handler) {
	handlers.remove(&handler);
	if(running)
		handler.onCleanup(*this);
}

bool Engine::hasManager(Manager &handler) {
	return count(handlers.begin(), handlers.end(), &handler);
}

}
